#ifndef _U2SceneManager_H__
#define _U2SceneManager_H__


#include "U2PreRequest.h"
#include "U2MemoryAllocatorConfig.h"
#include "U2Matrix4.h"
#include "U2TestRenderable.h"



U2EG_NAMESPACE_BEGIN


class U2RenderSystem;
class U2Renderable;
class U2RenderObjectListener;
class U2Camera;
class U2Viewport;
class U2AutoParamDataSource;


class _U2Share U2SceneManager : public SceneMgrAlloc
{
public:
    U2SceneManager(const U2String& name);
    virtual ~U2SceneManager();

	/** Mutex to protect the scene graph from simultaneous access from
		multiple threads.
	@remarks
		If you are updating the scene in a separate thread from the rendering
		thread, then you should lock this mutex before making any changes to 
		the scene graph - that means creating, modifying or deleting a
		scene node, or attaching / detaching objects. It is <b>your</b> 
		responsibility to take out this lock, the detail methods on the nodes
		will not do it for you (for the reasons discussed below).
	@par
		Note that locking this mutex will prevent the scene being rendered until 
		it is unlocked again. Therefore you should do this sparingly. Try
		to create any objects you need separately and fully prepare them
		before doing all your scene graph work in one go, thus keeping this
		lock for the shortest time possible.
	@note
		A single global lock is used rather than a per-node lock since 
		it keeps the number of locks required during rendering down to a 
		minimum. Obtaining a lock, even if there is no contention, is not free
		so for performance it is good to do it as little as possible. 
		Since modifying the scene in a separate thread is a fairly
		rare occurrence (relative to rendering), it is better to keep the 
		locking required during rendering lower than to make update locks
		more granular.
	*/
	U2_MUTEX(sceneGraphMutex)

	/** Return the instance name of this SceneManager. */
	const U2String& getName(void) const { return mName; }

	/** Retrieve the type name of this scene manager.
	@remarks
		This method has to be implemented by subclasses. It should
		return the type name of this SceneManager which agrees with 
		the type name of the SceneManagerFactory which created it.
	*/
	virtual const U2String& getTypeName(void) const = 0;

    /** Notifies the scene manager of its destination render system
    @remarks
        Called automatically by RenderSystem::addSceneManager
        this method simply notifies the manager of the render
        system to which its output must be directed.
    @param
        sys Pointer to the RenderSystem subclass to be used as a render target.
    */
    virtual void _setDestinationRenderSystem(U2RenderSystem* sys);

	/** Empties the entire scene, inluding all SceneNodes, Entities, Lights, 
        BillboardSets etc. Cameras are not deleted at this stage since
        they are still referenced by viewports, which are not destroyed during
        this process.
    */
    virtual void clearScene(void);

    /** Prompts the class to send its contents to the renderer.
    @remarks
        This method prompts the scene manager to send the
        contents of the scene it manages to the rendering
        pipeline, possibly preceded by some sorting, culling
        or other scene management tasks. Note that this method is not normally called
        directly by the user application; it is called automatically
        by the Ogre rendering loop.
    @param camera Pointer to a camera from whose viewpoint the scene is to
        be rendered.
    @param vp The target viewport
    */
    virtual void _renderScene(U2Camera* camera, U2Viewport* vp);

	/** Gets the current viewport being rendered (advanced use only, only 
		valid during viewport update. */
	U2Viewport* getCurrentViewport(void) const { return mCurrentViewport; }

    /** Internal method for setting the destination viewport for the next render. */
    virtual void setViewport(U2Viewport *vp);

    /** Creates a camera to be managed by this scene manager.
    @remarks
        This camera must be added to the scene at a later time using
        the attachObject method of the SceneNode class.
    @param
        name Name to give the new camera.
    */
    virtual U2Camera* createCamera(const U2String& name);

    /** Retrieves a pointer to the named camera.
	@note Throws an exception if the named instance does not exist
    */
    virtual U2Camera* getCamera(const U2String& name) const;

	/** Returns whether a camera with the given name exists.
	*/
	virtual bool hasCamera(const U2String& name) const;

    /** Removes a camera from the scene.
    @remarks
        This method removes a previously added camera from the scene.
        The camera is deleted so the caller must ensure no references
        to it's previous instance (e.g. in a SceneNode) are used.
    @param
        cam Pointer to the camera to remove
    */
    virtual void destroyCamera(U2Camera *cam);

    /** Removes a camera from the scene.
    @remarks
        This method removes an camera from the scene based on the
        camera's name rather than a pointer.
    */
    virtual void destroyCamera(const U2String& name);

    /** Removes (and destroys) all cameras from the scene.
    @remarks
        Some cameras are internal created to dealing with texture shadow,
        their aren't supposed to destroy outside. So, while you are using
        texture shadow, don't call this method, or you can set the shadow
        technique other than texture-based, which will destroy all internal
        created shadow cameras and textures.
    */
    virtual void destroyAllCameras(void);


protected:

    /// Internal method for firing when rendering a single object.
    virtual void fireRenderSingleObject(U2Renderable* rend);

    /** Internal utility method for rendering a single object. 
    @remarks
        Assumes that the pass has already been set up.
    @param rend The renderable to issue to the pipeline.
    */
    virtual void renderSingleObject(U2Renderable* rend);

    /** Internal method for creating the AutoParamDataSource instance. */
    virtual U2AutoParamDataSource* createAutoParamDataSource(void) const;

    virtual void setViewMatrix(const U2Matrix4& m);



protected:
    typedef std::vector<U2RenderObjectListener*>    U2RenderObjectListenerList;
    U2RenderObjectListenerList                      mRenderObjectListeners;

    /// Instance name
    U2String                                        mName;
    /// The rendering system to send the scene to
    U2RenderSystem                                  *mDestRenderSystem;
    /// Current Viewport
    U2Viewport                                      *mCurrentViewport;

    typedef std::map<U2String, U2Camera*>           U2CameraList;
    /** Central list of cameras - for easy memory management and lookup.
    */
    U2CameraList                                    mCameras;

    /// Camera in progress
    U2Camera*                                       mCameraInProgress;

    /// Utility class for calculating automatic parameters for gpu programs
    U2AutoParamDataSource*                          mAutoParamDataSource;
    unsigned long                                   mLastFrameNumber;
    U2Matrix4                                       mCachedViewMatrix;

    U2TestRenderable                                mTestRenderable;
};




/// Bitmask containing scene types
typedef u2uint16 SceneTypeMask;

/** Classification of a scene to allow a decision of what type of
SceenManager to provide back to the application.
*/
enum SceneType
{
	ST_GENERIC          = 1,
// 	ST_EXTERIOR_CLOSE = 2,
// 	ST_EXTERIOR_FAR = 4,
// 	ST_EXTERIOR_REAL_FAR = 8,
// 	ST_INTERIOR = 16
};

/** Structure containing information about a scene manager. */
struct SceneManagerMetaData
{
	/// A globally unique string identifying the scene manager type
	U2String            typeName;
	/// A text description of the scene manager
	U2String            description;
	/// A mask describing which sorts of scenes this manager can handle
	SceneTypeMask       sceneTypeMask;
	/// Flag indicating whether world geometry is supported
	bool                worldGeometrySupported;
};



/** Class which will create instances of a given U2SceneManager. */
class _U2Share U2SceneManagerFactory : public SceneMgrAlloc
{
protected:
	mutable SceneManagerMetaData    mMetaData;
	mutable bool                    mMetaDataInit;
	/// Internal method to initialise the metadata, must be implemented
	virtual void initMetaData(void) const = 0;

public:
	U2SceneManagerFactory() : mMetaDataInit(true) {}
	virtual ~U2SceneManagerFactory() {}

	/** Get information about the U2SceneManager type created by this factory. */
	virtual const SceneManagerMetaData& getMetaData(void) const 
	{
		if (mMetaDataInit)
		{
			initMetaData();
			mMetaDataInit = false;
		}
		return mMetaData; 
	}

	/** Create a new instance of a U2SceneManager.
	@remarks
	    Don't call directly, use U2SceneManagerEnumerator::createSceneManager.
	*/
	virtual U2SceneManager* createInstance(const U2String& instanceName) = 0;

	/** Destroy an instance of a U2SceneManager. */
	virtual void destroyInstance(U2SceneManager* instance) = 0;

};



U2EG_NAMESPACE_END


#endif
